package model.framework;

import model.config.Config;

public class BlockType {
	
	// [rotations][x][y]
	private int[][][] block;
	
	/**
	 * Creates a new block with the given int array
	 * Calculates each rotation using {@link #rotateRight(int[][])}
	 * Removes rotations if they are the same, using {@link #rotationEquals(int[][], int[][])}
	 * @param newBlock
	 */
	public BlockType(int[][] newBlock){
		/* create all rotations */
		block = new int[4][newBlock.length][newBlock[0].length];			
		block[0] = newBlock;
		block[1] = rotateRight(block[0]);
		block[2] = rotateRight(block[1]);
		block[3] = rotateRight(block[2]);
		
		/* if some rotations turn out to be the same, remove then */
		if (rotationEquals(block[0], block[1])){
			int[][][] actualBlock = new int[1][newBlock.length][newBlock[0].length];
			actualBlock[0] = block[0];
			block = actualBlock;
		} else if (rotationEquals(block[0], block[2])){
			int[][][] actualBlock = new int[2][newBlock.length][newBlock[0].length];
			actualBlock[0] = block[0];
			actualBlock[1] = block[1];
			block = actualBlock;
		}
	}
	
	/**
	 * Checks if two bidimensional arrays are exactly the same
	 * @param first
	 * @param second
	 * @return
	 */
	private boolean rotationEquals(int[][] first, int[][] second){
		boolean same = first.length == second.length && first[0].length == second[0].length;
		int i = 0;
		while (i < first.length && same){
			int j = 0;
			while (same && j < first[i].length){
				same = first[i][j] == second[i][j];
				j++;
			}
			i++;
		}
		return same;
	}
	
	/**
	 * Returns a new array, rotated 90 degrees to the right
	 * @param original
	 * @return
	 */
	private int[][] rotateRight(int [][] original){
		int originalX = original.length;
		int originalY = original[0].length;
		int[][] result = new int[originalY][originalX];
		for (int x = 0; x < originalX; x++){
			for (int y = 0; y < originalY; y++){
				result[y][originalX -1-x] = original[x][y];
			}
		}
		return result;
	}
	
	/**
	 * The entire block for the given rotation
	 * @param rotation
	 * @return
	 */
	public int[][] getDescription( int rotation ) {
		return block[ rotation ];
	}
	
	/**
	 * The height for the given rotation
	 * @param rotation
	 * @return
	 */
	public int getHeight( int rotation ) {
		return  block[ rotation ][0].length;
	}
	
	/**
	 * The width for the given rotation
	 * @param rotation
	 * @return
	 */
	public int getWidth( int rotation ) {
		return block[ rotation ].length;
	}
	
	/**
	 * An array of the lower most parts of the block
	 * @param rotation
	 * @return
	 */
	public int[] getSurface( int rotation ) {
		int[][] blockDimensions = block[ rotation ];
		
		int[] surface = new int[ blockDimensions.length ];
		
		for( int indexX = 0; indexX < blockDimensions.length; indexX++ ) {
			
			/* Loop through it bottom to top */
			for( int indexY = 0; indexY < blockDimensions[ indexX ].length; indexY++ ) {
				
				/* There is a ( piece of ) block here! Better make a note of it */
				if( blockDimensions[ indexX ][ indexY ] == 0 ) {

					/* Remember the current height */
					surface[ indexX ] = indexY + 1;
					
				} else if ( blockDimensions[ indexX ][ indexY ] == 1 ) {
					break;
				}
			}
		}
		
		return surface;
	}
	
	/**
	 * @return the number of rotations
	 */
	public int getRotations() {
		return block.length;
	}
	
	/**
	 * Returns a String representation of the Block
	 */
	public String toString() {
		/* The return String */
		String blockString = "";
		Config.p("Number of rotations: " + block.length);
		for (int rot = 0 ; rot < block.length; rot++){
			blockString += ("Rotation " + (rot) + ": \n");
			int yLength = block[rot][0].length;
			for (int y = 0; y < yLength; y++){
				int xLength = block[rot].length;
				for (int x = 0; x < xLength; x++){
					blockString += "" + block[rot][x][yLength-1-y];
				}
				blockString += "\n";
			}
			blockString += "--------------------";
			if (rot < block.length-1){
				blockString += "\n";
			}
		}
		return blockString;
	}
	
	/**
	 * return true if this block is the same as the given block, or if one of the rotations of the given block is the same
	 */
	public boolean equals(Object o){
		boolean equal = o.getClass() == this.getClass();
		if (equal) {
			BlockType other = (BlockType) o;
			
			/* does it have the same number of rotations */
			equal = this.block.length == other.block.length;
			
			/* try every rotation to see if this block is just a rotated version */
			int rot = 0;
			boolean found = false;
			while (equal && !found && rot < block.length){
				found = rotationEquals(this.block[0], other.block[rot]);
				rot++;
			}
			
			/* if found is true, on of the rotations is the same as our block */
			equal = found;
		}
		return equal;
	}
}
